/**
 *  Copyright 2002-2009 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package foo.bar.wiki.web.action;

import foo.bar.wiki.domain.User;
import foo.bar.wiki.services.exception.BusinessLogicException;
import foo.bar.wiki.services.exception.BusinessLogicExecution;
import foo.bar.wiki.services.exception.ThemeNotAvailableException;
import foo.bar.wiki.services.exception.ThemeMessageNotAvailableException;
import foo.bar.wiki.web.extension.CustomActionBeanContext;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.integration.spring.SpringBean;
import net.sourceforge.stripes.validation.SimpleError;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.context.MessageSource;
import org.springframework.ui.context.Theme;
import org.springframework.ui.context.ThemeSource;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.transaction.TransactionStatus;

/**
 * A base action that provides the following :-
 *
 * <ul>
 *  <li>accessor methods for {@link ActionBeanContext}</li>
 *  <li>methods to access current non-blog's theme path (public and private eg. under /WEB-INF/*)</li>
 *  <li>methods to access blog's theme path (public only)</li>
 *  <li>manipulate request parameters<li>
 *  <li>manipulate request attribute</li>
 *  <li>manipulate session attribute</li>
 *  <li>set success ajax header</li>
 *  <li>get current subject</li>
 *  <li>get current user</li>
 *  <li>template method for handling business logic exception</li>
 * </ul>
 *
 * @see {@link #getContext()}
 * @see {@link #getContext()}
 * @see {@link #getRequestParameter(String)}
 * @see {@link #getRequestAttribute(String)}
 * @see {@link #setRequestAttribute(String, Object)}
 * @see {@link #getSessionAttribute(String)}
 * @see {@link #setSessionAttribute(String, Object)}
 * @see {@link #setAjaxRequestSuccessHeader()}
 * @see {@link #getCurrentSubject()}
 * @see {@link #getCurrentUser()}
 *
 * @author tmjee
 * @version $Date$ $Id$
 */
public abstract class BaseActionBean implements ActionBean, TransactionStatusAware {

    private static final Log LOG = LogFactory.getLog(BaseActionBean.class);


    private CustomActionBeanContext actionBeanContext;
    private ThemeResolver themeResolver;
    private ThemeSource themeSource;

    private TransactionStatus transactionStatus;



    @SpringBean("themeResolver")
    public void setWikiThemeResolver(ThemeResolver themeResolver) {
        this.themeResolver = themeResolver;
    }

    @SpringBean("themeSource")
    public void setWikiThemeSource(ThemeSource themeSource) {
        this.themeSource = themeSource;
    }





    // ============================================================================

    /**
     * Set the current TransactionStatus through TransactionInterceptor.
     *
     * @param transactionStatus
     */
    public void setCurrentTransactionStatus(TransactionStatus transactionStatus) {
        this.transactionStatus = transactionStatus;
    }

    /**
     * Retrieve the current TransactionStatus.
     * @return
     */
    public TransactionStatus getCurrentTransactionStatus() {
        return transactionStatus;
    }




    /**
     * Set {@link net.sourceforge.stripes.action.ActionBeanContext}.
     *
     * @param actionBeanContext
     */
    public void setContext(ActionBeanContext actionBeanContext) {
        this.actionBeanContext = (CustomActionBeanContext) actionBeanContext;
    }

    /**
     * Get {@link net.sourceforge.stripes.action.ActionBeanContext}
     *
     * @return {@link net.sourceforge.stripes.action.ActionBeanContext}
     */
    public CustomActionBeanContext getContext() {
        return actionBeanContext;
    }


    /**
     * append <code>resource1</code> to <code>resource2</code> making sure there's no
     * missing or duplicate '/' during the combination process.
     *
     * @param resource1
     * @param resource2
     * @return String
     */
    private String append(String resource1, String resource2) {
        resource1 = resource1 == null ? "" : resource1.trim();
        resource2 = resource2 == null ? "" : resource2.trim();
        resource1 = resource1.endsWith("/") ? resource1 : resource1+"/";
        resource2 = resource2.startsWith("/") ? resource2.substring(1) : resource2;
        return resource1 + resource2;
    }

    /**
     * return the current blog's theme path.
     *
     * @return String
     */
    public String getThemePath() {
        String theme = themeResolver.resolveThemeName(actionBeanContext.getRequest());
        Theme _theme = themeSource.getTheme(theme);
        MessageSource themeMessageSource = _theme.getMessageSource();
        return themeMessageSource.getMessage("public.root", null, actionBeanContext.getLocale());
    }


    public String getThemePath(String theme) {
        Theme _theme = themeSource.getTheme(theme);
        if (theme == null)
            throw new ThemeNotAvailableException("Theme ["+theme+"] is not available");
        MessageSource themeMessageSource = _theme.getMessageSource();
        String message = themeMessageSource.getMessage("public.root", null, actionBeanContext.getLocale());
        if (message == null)
            throw new ThemeMessageNotAvailableException("Message public.root is not available in theme ["+theme+"]");
        return message;
    }


    public String getDefaultThemePath() {
        Theme _theme = themeSource.getTheme("Default");
        MessageSource themeMessageSource = _theme.getMessageSource();
        return themeMessageSource.getMessage("public.root", null, actionBeanContext.getLocale());
    }

    /**
     * create the full path for <code>resource</code> based on the blog theme path.
     *
     * @param resource
     * @return String
     */
    protected String generateThemePath(String resource) {
        return append(getThemePath(), resource);
    }

    protected String generateDefaultThemePath(String resource) {
        return append(getDefaultThemePath(), resource);
    }

    protected String generateThemePath(String theme, String resource) {
        return append(getThemePath(theme), resource);
    }

    /**
     * get the current request parameter based on <code>parameterName</code>
     *
     * @param parameterName
     * @return String
     */
    protected String getRequestParameter(String parameterName) {
        return getContext().getRequest().getParameter(parameterName);
    }

    /**
     * get the current request attribute based on <code>attributeName</code>.
     *
     * @param attributeName
     * @return
     */
    protected Object getRequestAttribute(String attributeName) {
        return getContext().getRequest().getAttribute(attributeName);
    }

    /**
     * store <code>attributeName</code> and <code>attributeValue</code> pairs into the
     * current request
     *
     * @param attributeName
     * @param attributeValue
     */
    protected void setRequestAttribute(String attributeName, Object attributeValue) {
        getContext().getRequest().setAttribute(attributeName, attributeValue);
    }

    /**
     * retrieve the <code>attributeName</code> from current session.
     *
     * @param attributeName
     * @return Object
     */
    protected Object getSessionAttribute(String attributeName) {
        return getContext().getRequest().getSession(true).getAttribute(attributeName);
    }

    /**
     * store <code>attributeName</code> and <code>attributeValue</code> pairs into the
     * current session.
     *
     * @param attributeName
     * @param attributeValue
     */
    protected void setSessionAttribute(String attributeName, Object attributeValue) {
        getContext().getRequest().getSession(true).setAttribute(attributeName, attributeValue);
    }


    /**
     * Retrieve a unique action id for this {@link net.sourceforge.stripes.action.ActionBean},
     * normally used as prefix for session or request attribute name.
     *
     * @return
     */
    protected String getActionId() {
        return getClass().getName()+"_";
    }

    /**
     * Set into the current request header ("X-AjaxRequestStatus") a value "true", used in
     * ajax request when the result needs to be identified if it ended successfully or not.
     */
    protected void setAjaxRequestSuccessHeader() {
        getContext().getResponse().setHeader("X-AjaxRequestStatus", "true");
    }

    /**
     * return the current jSecurity's {@link org.apache.shiro.subject.Subject}.
     * @return {@link org.apache.shiro.subject.Subject}
     */
    protected Subject getCurrentSubject() {
        return SecurityUtils.getSubject();
    }

    /**
     * return the current {@link foo.bar.wiki.domain.User}
     * @return {@link foo.bar.wiki.domain.User}
     */
    protected User getCurrentUser() {
        return (User) getCurrentSubject().getPrincipal();
    }

    
    /**
     * Returns a callback object {@link foo.bar.wiki.services.exception.BusinessLogicException} that handles
     * {@link BusinessLogicException} and it's decendent (if thrown) by adding a message and doing a request
     * forward to the source page.
     *
     * @see {@link foo.bar.wiki.services.exception.BusinessLogicException}
     *
     * @param execution
     * @return {@link Resolution}
     * @throws Throwable
     */
    protected Resolution handleBusinessLogicException(BusinessLogicExecution execution) throws Throwable {
        try {
            return execution.execute();
        }
        catch(BusinessLogicException e) {
            getCurrentTransactionStatus().setRollbackOnly();
            getContext().getValidationErrors().addGlobalError(new SimpleError(e.getMessage()));
            return getContext().getSourcePageResolution();
        }
    }
}


